package mx.itesm.rmroman.proyectojuegomavi;

import android.content.Context;
import android.graphics.Rect;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;

import org.andengine.engine.camera.Camera;
import org.andengine.engine.camera.hud.controls.AnalogOnScreenControl;
import org.andengine.engine.camera.hud.controls.BaseOnScreenControl;
import org.andengine.entity.IEntity;
import org.andengine.entity.modifier.ParallelEntityModifier;
import org.andengine.entity.modifier.RotationByModifier;
import org.andengine.entity.modifier.ScaleModifier;
import org.andengine.entity.primitive.Rectangle;
import org.andengine.entity.scene.CameraScene;
import org.andengine.entity.scene.Scene;
import org.andengine.entity.scene.background.Background;
import org.andengine.entity.scene.background.SpriteBackground;
import org.andengine.entity.sprite.Sprite;
import org.andengine.entity.text.Text;
import org.andengine.input.touch.TouchEvent;
import org.andengine.opengl.font.Font;
import org.andengine.opengl.font.FontFactory;
import org.andengine.opengl.font.IFont;
import org.andengine.opengl.texture.ITexture;
import org.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.andengine.opengl.util.GLState;
import org.andengine.util.adt.align.HorizontalAlign;

import java.util.ArrayList;

/**
 * La escena de JUEGO, es una de las clases más grandes, implementa la lógica completa.
 *
 * @author Roberto Martínez Román
 */
public class EscenaJuego extends EscenaBase implements SensorEventListener // Para el sensor
{
    public static final int MAX_VELOCIDAD = 20;
    private Sprite spriteFondo; //(el fondo de la escena, estático)
    private Sprite spriteMoscaMala; // El enemigo

    // Arreglo de moscas
    private ArrayList<Sprite> lista;
    private final int NUMERO_MOSCAS = 10;

    // 'offset' para mover la mosca mala en cada actualización
    private int dx = 4;
    private int dy = 3;

    // El matamoscas
    private Sprite spriteMatamoscas;    // El 'personaje'

    // El joystick virtual
    private AnalogOnScreenControl control;

    // ACUMULADOR de tiempo
    private float tiempoAcumulado;

    // Sensor de GRAVEDAD
    private SensorManager admSensores; // Administra TODOS los sensores del dispositivo
    private Sensor sensorGravedad;      // El sensor específico de gravedad

    // Condición de terminación
    private boolean juegoTermina = false;

    // *** La Escena que se ve cuando termina el juego
    private Scene escenaTermina;

    // PAUSAR
    private boolean juegoEnPausa;
    private Scene escenaPausa;  // Se muestra mientras está pausado

    @Override
    public void crearEscena() {
        // Creamos el sprite de manera óptima
        spriteFondo = new Sprite(0,0, admRecursos.regionFondoJuego,admRecursos.vbom) {
            @Override
            protected void preDraw(GLState pGLState, Camera pCamera) {
                super.preDraw(pGLState, pCamera);
                pGLState.enableDither();
            }
        };
        // Configuración de la imagen
        spriteFondo.setPosition(ControlJuego.ANCHO_CAMARA/2,ControlJuego.ALTO_CAMARA/2);

        // Crea el fondo de la pantalla
        SpriteBackground fondo = new SpriteBackground(1,0.3f,0.3f,spriteFondo);
        setBackground(fondo);
        setBackgroundEnabled(true);

        agregarMuchasMoscas();
        agregarMosca();
        agregarMatamoscas();
        agregarJoystick();

        inicializarSensor();    // Para configurar el sensor

        crearEscenaTermina();
        // Pausa
        crearEscenaPausa();
    }

    private void crearEscenaPausa() {
        // Crea la escena que se mostrará
        escenaPausa = new Scene();
        // No muestra fondo
        escenaPausa.setBackgroundEnabled(false);
        // Un recuadro como fondo para mostrar los letreros
        Rectangle cuadro = new Rectangle(ControlJuego.ANCHO_CAMARA/2,ControlJuego.ALTO_CAMARA/2,
                ControlJuego.ANCHO_CAMARA/2,ControlJuego.ALTO_CAMARA/2,admRecursos.vbom) {
            @Override
            public boolean onAreaTouched(TouchEvent pSceneTouchEvent, float pTouchAreaLocalX, float pTouchAreaLocalY) {
                // El usuario hizo touch, REANUDAR el juego
                if (pSceneTouchEvent.isActionUp()) {
                    juegoEnPausa = false;
                    EscenaJuego.this.setIgnoreUpdate(false);    // REANUDA llamadas a onManagedUpdate
                    return true;
                }
                return super.onAreaTouched(pSceneTouchEvent, pTouchAreaLocalX, pTouchAreaLocalY);
            }
        };
        cuadro.setColor(1,1,1,0.8f);
        escenaPausa.attachChild(cuadro);

        // Crea los letreros
        IFont tipo = crearTipoLetra("cartoon.ttf");
        Text txtPierdes = new Text(ControlJuego.ANCHO_CAMARA/2,5*ControlJuego.ALTO_CAMARA/8,
                tipo,"p a u s a",22,admRecursos.engine.getVertexBufferObjectManager());
        txtPierdes.setColor(0,0,0);
        escenaPausa.attachChild(txtPierdes);
        Text txtRegresar = new Text(ControlJuego.ANCHO_CAMARA/2,ControlJuego.ALTO_CAMARA/2,
                tipo,"click para reanudar",20,admRecursos.engine.getVertexBufferObjectManager());
        txtRegresar.setColor(0, 0, 0);
        escenaPausa.attachChild(txtRegresar);
        // Registra el recuadro completo para regresar
        escenaPausa.registerTouchArea(cuadro);
    }

    // Crea y regresa un font que carga desde un archivo .ttf  (http://www.1001freefonts.com, http://www.1001fonts.com/)
    private Font crearTipoLetra(String archivoTipo) {

        // La imagen que contiene cada símbolo
        final ITexture fontTexture = new BitmapTextureAtlas(admRecursos.engine.getTextureManager(),1024,1024);

        // Carga el archivo, tamaño 36, antialias y color
        Font tipoLetra = FontFactory.createFromAsset(admRecursos.engine.getFontManager(),
                fontTexture, admRecursos.actividadJuego.getAssets(), archivoTipo, 44, true, 0xFF00FF00);
        tipoLetra.load();
        tipoLetra.prepareLetters("Regrsa lmnupidPd,ck".toCharArray());

        return tipoLetra;
    }

    // Crea la escena que se mostrará cuando termina (pierde) el juego
    private void crearEscenaTermina() {
        // Crea la escena que se mostrará
        escenaTermina = new Scene();
        // No muestra fondo
        escenaTermina.setBackgroundEnabled(false);
        // Un recuadro como fondo para mostrar los letreros
        Rectangle cuadro = new Rectangle(ControlJuego.ANCHO_CAMARA/2,ControlJuego.ALTO_CAMARA/2,
                ControlJuego.ANCHO_CAMARA/2,ControlJuego.ALTO_CAMARA/2,admRecursos.vbom) {
            @Override
            public boolean onAreaTouched(TouchEvent pSceneTouchEvent, float pTouchAreaLocalX, float pTouchAreaLocalY) {
                // El usuario hizo touch, regresar al menú principal
                if (pSceneTouchEvent.isActionUp()) {
                    regresarAMenu();
                    return true;
                }
                return super.onAreaTouched(pSceneTouchEvent, pTouchAreaLocalX, pTouchAreaLocalY);
            }
        };
        cuadro.setColor(1,1,1,0.8f);
        escenaTermina.attachChild(cuadro);

        // Crea los letreros
        IFont tipo = crearTipoLetra("bytes.ttf");
        Text txtPierdes = new Text(ControlJuego.ANCHO_CAMARA/2,5*ControlJuego.ALTO_CAMARA/8,
                tipo,"Pierdes, click para",22,admRecursos.engine.getVertexBufferObjectManager());
        txtPierdes.setColor(0,0,0);
        escenaTermina.attachChild(txtPierdes);
        Text txtRegresar = new Text(ControlJuego.ANCHO_CAMARA/2,ControlJuego.ALTO_CAMARA/2,
                tipo,"regresar al menu",20,admRecursos.engine.getVertexBufferObjectManager());
        txtRegresar.setColor(0, 0, 0);
        escenaTermina.attachChild(txtRegresar);
        // Registra el recuadro completo para regresar
        escenaTermina.registerTouchArea(cuadro);
    }

    private void inicializarSensor() {
        admSensores = (SensorManager)admRecursos.actividadJuego.getSystemService(Context.SENSOR_SERVICE);
        sensorGravedad = admSensores.getDefaultSensor(Sensor.TYPE_GRAVITY);
        if (sensorGravedad!=null) {
            admSensores.registerListener(this,sensorGravedad,SensorManager.SENSOR_DELAY_GAME);
        } else {
            Log.i("SENSOR", "No hay sensor en tu dispositivo");
        }
    }

    // El joystick virtual para mover el matamoscas
    private void agregarJoystick() {
        control = new AnalogOnScreenControl(64,64,admRecursos.camara,
                admRecursos.regionFondoControl, admRecursos.regionBotonControl,
                0.03f,100,admRecursos.vbom,new AnalogOnScreenControl.IAnalogOnScreenControlListener() {
            @Override
            public void onControlClick(AnalogOnScreenControl pAnalogOnScreenControl) {
                // Click al botón
                float x = (float)(Math.random()*ControlJuego.ANCHO_CAMARA);
                float y = (float)(Math.random()*ControlJuego.ALTO_CAMARA);
                spriteMatamoscas.setPosition(x,y);
            }

            @Override
            public void onControlChange(BaseOnScreenControl pBaseOnScreenControl, float pValueX, float pValueY) {
                // Movimiento del control pValueX-horizontal y pValueY-vertical
                float x = spriteMatamoscas.getX()+20*pValueX;
                if (x>ControlJuego.ANCHO_CAMARA || x<0) {
                    x = spriteMatamoscas.getX();
                }
                float y = spriteMatamoscas.getY()+20*pValueY;
                if (y>ControlJuego.ALTO_CAMARA || y<0 ) {
                    y = spriteMatamoscas.getY();
                }
                spriteMatamoscas.setPosition(x, y);
            }
        });
        final Sprite botonControl = control.getControlKnob();
        botonControl.setScale(1.7f);
        setChildScene(control);
    }

    private void agregarMatamoscas() {
        // Creamos el sprite de manera óptima
        spriteMatamoscas = new Sprite(ControlJuego.ANCHO_CAMARA/4,ControlJuego.ALTO_CAMARA/4,
                admRecursos.regionMatamoscas,admRecursos.vbom) {
            @Override
            protected void preDraw(GLState pGLState, Camera pCamera) {
                super.preDraw(pGLState, pCamera);
                pGLState.enableDither();
            }
        };
        // Se agrega a la escena
        attachChild(spriteMatamoscas);
    }

    // Ciclo principal del juego
    // Este método se repite varias veces por segundo, aprox. fps
    @Override
    protected void onManagedUpdate(float pSecondsElapsed) {
        super.onManagedUpdate(pSecondsElapsed);

        // Pregunta si debe terminar
        if (juegoTermina) {     // Se prendió la bandera?
            //regresarAMenu();    // Terminar
            setIgnoreUpdate(true);  // Ya no ejecuta este método
            terminar(); // Mostrar la escena de juego termina
            return;
        }

        // Manejo del tiempo
        tiempoAcumulado += pSecondsElapsed;
        if (tiempoAcumulado>2) {    // ¿Pasaron 2 segundos?
            // Crea una nueva mosca en una posición aleatoria
            float xa = (float)(Math.random()*ControlJuego.ANCHO_CAMARA);
            float ya = (float)(Math.random()*ControlJuego.ALTO_CAMARA);
            Sprite nueva = new Sprite(xa, ya,
                    admRecursos.regionMosca, admRecursos.vbom);
            attachChild(nueva); // AGREGA a la ESCENA
            lista.add(nueva);   // Agrega en la lista
            tiempoAcumulado=0;  // Reinicia el contador de tiempo
            if (Math.abs(dx)< MAX_VELOCIDAD) {
                dx *= 1.3f;
            }
        }

        // Visita cada 'mosca' y la gira 2 grados
        for(Sprite sp:lista) {
            sp.setRotation(sp.getRotation()+2);
        }
        // Prueba choques
        for(int i=lista.size()-1; i>=0; i--) {
            Sprite sp = lista.get(i);
            if (spriteMatamoscas.collidesWith(sp) ) {   // ¿Chocó?
                sp.detachSelf(); // detachChild(sp);    // Ambas son equivalentes
                lista.remove(sp);
                // CONTAR
            }
        }
        // Prueba choque contra MOSCA enemiga
        if (spriteMatamoscas.collidesWith(spriteMoscaMala)) {
            // Perdió, hace que el matamoscas desaparezca y regresa al menú

            // En dos segundos pasa de escala 1 a escala 0
            ScaleModifier escala = new ScaleModifier(1,1,0);
            // Hace que el matamoscas rote, 10 vueltas en 2 segundos
            RotationByModifier rotacion = new RotationByModifier(1, (float) (20 * Math.PI));
            // Hace que se escale y rote al mismo tiempo
            ParallelEntityModifier paralelo = new ParallelEntityModifier(escala,rotacion){
                @Override
                protected void onModifierFinished(IEntity pItem) {  // Al terminar la rotación
                    super.onModifierFinished(pItem);
                    juegoTermina =true;
                }
            };

            // Ejecuta el modificador sobre el sprite
            spriteMatamoscas.registerEntityModifier(paralelo);
        }
    }

    private void terminar() {
        setChildScene(escenaTermina,false,true,true);
    }

    private void agregarMuchasMoscas() {
        lista = new ArrayList<>(NUMERO_MOSCAS);
        for(int i=1; i<=NUMERO_MOSCAS; i++) {
            float xa = (float)(Math.random()*ControlJuego.ANCHO_CAMARA);
            float ya = (float)(Math.random()*ControlJuego.ALTO_CAMARA);
            Sprite unaMosca = new Sprite(xa, ya,
                    admRecursos.regionMosca, admRecursos.vbom);
            attachChild(unaMosca); // AGREGA a la ESCENA
            lista.add(unaMosca);    // Guarda en la lista
        }
    }

    // Agrega la mosca MALA
    private void agregarMosca() {
        // Creamos el sprite de manera óptima
        spriteMoscaMala = new Sprite(ControlJuego.ANCHO_CAMARA/2,ControlJuego.ALTO_CAMARA/2,
                admRecursos.regionMoscaMala,admRecursos.vbom) {
            @Override
            protected void preDraw(GLState pGLState, Camera pCamera) {
                super.preDraw(pGLState, pCamera);
                pGLState.enableDither();
            }

            @Override
            protected void onManagedUpdate(float pSecondsElapsed) {
                super.onManagedUpdate(pSecondsElapsed);
                // Aquí va toda la animación de esta mosca (también puede ser en onManagedUpdate)

                // Mueve las coordenadas sumando dx,dy a la posición actual
                float px = spriteMoscaMala.getX()+dx;
                float py = spriteMoscaMala.getY()+dy;
                spriteMoscaMala.setPosition(px,py);
                // Prueba los límites de la pantalla
                if (px>=ControlJuego.ANCHO_CAMARA || px<=0) {
                    dx = -dx;
                }
                if (py>=ControlJuego.ALTO_CAMARA || py<=0) {
                    dy = -dy;
                }
            }
        };
        // Se agrega a la escena
        attachChild(spriteMoscaMala);
    }

    @Override
    public void onBackKeyPressed() {    // El usuario oprimió la tecla de BACK
        regresarAMenu();
    }

    // Regresar al menú principal
    private void regresarAMenu() {
        admEscenas.crearEscenaMenu();
        admEscenas.setEscena(TipoEscena.ESCENA_MENU);
        admEscenas.liberarEscenaJuego();
    }

    @Override
    public TipoEscena getTipoEscena() {
        return TipoEscena.ESCENA_JUEGO;
    }

    // Libera la memoria que ocupa esta escena
    @Override
    public void liberarEscena() {
        // La escena completa
        this.detachSelf();      // La escena se desconecta del engine
        this.dispose();         // Libera la memoria
    }

    // *** El sensor ha cambiado
    @Override
    public void onSensorChanged(SensorEvent event) {
        // Leer los valores del sensor y en base a ellos, mover el matamoscas

        // event.values es un arreglo de tipo float con 3 datos
        // cada elemento es la gravedad medida en x,y,z
        float nuevaX = spriteMatamoscas.getX() + event.values[1] * 5;
        if (nuevaX < ControlJuego.ANCHO_CAMARA && nuevaX >= 0) {
            spriteMatamoscas.setX(nuevaX);
        }

        float nuevaY = spriteMatamoscas.getY() - event.values[0] * 5;
        if (nuevaY < ControlJuego.ALTO_CAMARA && nuevaY >= 0) {
            spriteMatamoscas.setY(nuevaY);
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }
}
